﻿using System;
using System.Reflection;

namespace MarriageFertility.Helpers
{
    internal static class ReflectUtils
    {
        public static BindingFlags GetBindingFlags()
        {
            return BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
        }

        public static T CreateInstance<T>(params object[] args)
        {
            Type typeFromHandle = typeof(T);
            object obj = typeFromHandle.Assembly.CreateInstance(typeFromHandle.FullName, false, GetBindingFlags(), null, args, null, null);
            return (T)obj;
        }

        public static T ReflectField<T>(string key, object instance)
        {
            if (instance == null || string.IsNullOrEmpty(key)) return default;
            
            FieldInfo field = instance.GetType().GetField(key, GetBindingFlags());
            if (field != null && field.GetValue(instance) != null)
            {
                return (T)field.GetValue(instance);
            }
            return default;
        }

        public static object ReflectFieldAndSetValue(string key, object value, object instance)
        {
            if (instance == null || string.IsNullOrEmpty(key)) return null;

            FieldInfo field = instance.GetType().GetField(key, GetBindingFlags());
            if (field != null)
            {
                field.SetValue(instance, value);
            }
            return null;
        }

        public static object ReflectPropertyAndSetValue(string key, object value, object instance)
        {
            if (instance == null || string.IsNullOrEmpty(key)) return null;

            PropertyInfo property = instance.GetType().GetProperty(key, GetBindingFlags());
            if (property != null)
            {
                property.SetValue(instance, value);
            }
            return null;
        }

        public static T ReflectProperty<T>(string key, object instance)
        {
            if (instance == null || string.IsNullOrEmpty(key)) return default;

            PropertyInfo property = instance.GetType().GetProperty(key, GetBindingFlags());
            if (property != null)
            {
                return (T)property.GetValue(instance);
            }
            return default;
        }

        public static void ReflectMethodAndInvoke(string methodName, object instance, object[] paramObjects)
        {
            if (instance == null || string.IsNullOrEmpty(methodName)) return;

            MethodInfo method = instance.GetType().GetMethod(methodName, GetBindingFlags());
            method?.Invoke(instance, paramObjects);
        }

        public static T ReflectGetStaticField<T, K>(string fieldName)
        {
            if (string.IsNullOrEmpty(fieldName)) return default;

            FieldInfo[] fields = typeof(K).GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
            foreach (var fieldInfo in fields)
            {
                if (fieldInfo.Name == fieldName)
                {
                    return (T)fieldInfo.GetValue(null);
                }
            }
            return default;
        }

        public static object SetNotPublicVar(object instance, string varName, object newVar)
        {
            if (instance == null || string.IsNullOrEmpty(varName)) return null;

            Type type = instance.GetType();
            FieldInfo field = type.GetField(varName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            if (field == null)
            {
                field = type.BaseType?.GetField(varName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            }

            if (field != null)
            {
                field.SetValue(instance, newVar);
                return field.GetValue(instance);
            }
            return null;
        }
    }
}
